import { Component, EventEmitter, HostBinding, OnInit, Input, Output, OnDestroy } from '@angular/core';

import { TabDirective } from './tab.directive';
import { TabsetConfig } from './tabset.config';
@Component({
  selector: 'tabset',
  template: `
    <ul class="nav" [ngClass]="classMap" (click)="$event.preventDefault()">
        <li *ngFor="let tabz of tabs" [ngClass]="['nav-item', tabz.customClass || '']"
          [class.active]="tabz.active" [class.disabled]="tabz.disabled">
          <a href="javascript:void(0);" class="nav-link"
            [class.active]="tabz.active" [class.disabled]="tabz.disabled"
            (click)="click(tabz)">
            <span [ngTransclude]="tabz.headingRef">{{tabz.heading}}</span>
            <span *ngIf="tabz.removable">
              <span (click)="$event.preventDefault(); removeTab(tabz);" class="glyphicon glyphicon-remove-circle"></span>
            </span>
          </a>
        </li>
    </ul>
    <div class="tab-content">
      <ng-content></ng-content>
    </div>
  `
})
export class TabsetComponent implements OnInit, OnDestroy {
  @Input()
  public onTabClick: any;
  @Input()
  public afterTabClick: any;
  @Input()
  public selectNew: any;
  @Input()
  public plus: any;
  @Output()
  public addTabClick: EventEmitter<TabDirective> = new EventEmitter();
  /** if true tabs will be placed vertically */
  @Input()
  public get vertical(): boolean {
    return this._vertical;
  }
  public set vertical(value: boolean) {
    this._vertical = value;
    this.setClassMap();
  }
  /** if true tabs fill the container and have a consistent width */
  @Input()
  public get justified(): boolean {
    return this._justified;
  }
  public set justified(value: boolean) {
    this._justified = value;
    this.setClassMap();
  }

  /** navigation context class: 'tabs' or 'pills' */
  @Input()
  public get type(): string {
    return this._type;
  }
  public set type(value: string) {
    this._type = value;
    this.setClassMap();
  }

  @HostBinding('class.tab-container') public clazz = true;

  public tabs: TabDirective[] = [];
  public classMap: any = {};

  protected isDestroyed: boolean;
  protected _vertical: boolean;
  protected _justified: boolean;
  protected _type: string;
  public constructor(config: TabsetConfig) {
    Object.assign(this, config);
  }
  public ngOnInit() {
    if (this.plus) {
      const add = new TabDirective(this);
      add.active = false;
      add.select = this.addTabClick;
      add.heading = '+';
    }
  }
  public click(tabz): void {
    if (this.onTabClick) {
      if (!this.onTabClick(tabz)) {
        return;
      }
    }
    tabz.active = true;
    if (this.afterTabClick) {
      this.afterTabClick(tabz);
    }
  }
  public ngOnDestroy(): void {
    this.isDestroyed = true;
  }
  public getActiveTabIdx(): any {
    for (let idx = 0; idx < this.tabs.length; idx++) {
      if (this.tabs[idx].active) {
        return this.plus ? idx - 1 : idx;
      }
    }
    return 0;
  }
  public addTab(tab: TabDirective): void {
    this.tabs.push(tab);
    if (this.selectNew) {
      tab.active = true;
    } else {
      tab.active = this.tabs.length === 1 && tab.active !== false;
    }
  }
  public removeTabByIdx(idx: any): void {
    idx = this.plus ? idx + 1 : idx;
    const tab = this.tabs[idx];
    // Select a new tab if the tab to be removed is selected and not destroyed
    if (tab.active && this.hasAvailableTabs(idx)) {
      const newActiveIndex = this.getClosestTabIndex(idx);
      this.tabs[newActiveIndex].active = true;
    }

    tab.removed.emit(tab);
    this.tabs.splice(idx, 1);
  }
  public removeTagByIdxes(idxes: any[]): void {
    const tmp = [];
    idxes.forEach((idx: any) => {
      idx = this.plus ? idx + 1 : idx;
      const tab = this.tabs[idx];
      tmp.push(tab);
    });
    tmp.forEach((tab: TabDirective) => {
      const index = this.tabs.indexOf(tab);
      tab.removed.emit(tab);
      this.tabs.splice(index, 1);
    });
  }
  public remove(): void {
    let tmp = null;
    if (this.plus) {
      tmp = this.tabs[0];
    }
    this.tabs = [];
    if (tmp) {
      this.tabs.push(tmp);
    }
  }
  public getPlusTab(): TabDirective {
    let result = null;
    this.tabs.forEach((t: TabDirective) => {
      if (t.heading === '+') {
        result = t;
      }
    });
    return result;
  }
  public removeTab(tab: TabDirective): void {
    const index = this.tabs.indexOf(tab);
    if (index === -1 || this.isDestroyed) {
      return;
    }
    // Select a new tab if the tab to be removed is selected and not destroyed
    if (tab.active && this.hasAvailableTabs(index)) {
      const newActiveIndex = this.getClosestTabIndex(index);
      this.tabs[newActiveIndex].active = true;
    }

    tab.removed.emit(tab);
    this.tabs.splice(index, 1);
  }

  protected getClosestTabIndex(index: number): number {
    const tabsLength = this.tabs.length;
    if (!tabsLength) {
      return -1;
    }

    for (let step = 1; step <= tabsLength; step += 1) {
      const prevIndex = index - step;
      const nextIndex = index + step;
      if (this.tabs[prevIndex] && !this.tabs[prevIndex].disabled) {
        return prevIndex;
      }
      if (this.tabs[nextIndex] && !this.tabs[nextIndex].disabled) {
        return nextIndex;
      }
    }
    return -1;
  }

  protected hasAvailableTabs(index: number): boolean {
    const tabsLength = this.tabs.length;
    if (!tabsLength) {
      return false;
    }
    if (tabsLength === 2) {
      const idx = index === 0 ? 1 : 0;
      const tabAdd = this.tabs[idx];
      if (tabAdd.heading === '+') {
        return false;
      }
    }
    for (let i = 0; i < tabsLength; i += 1) {
      if (!this.tabs[i].disabled && i !== index) {
        return true;
      }
    }
    return false;
  }

  protected setClassMap(): void {
    this.classMap = {
      'nav-stacked': this.vertical,
      'nav-justified': this.justified,
      [`nav-${this.type}`]: true
    };
  }
}
